import java.io.FileInputStream;
import java.io.FileWriter;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Scanner;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.biojava3.core.sequence.RNASequence;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class BuildPhyloXMLTree {
	private LinkedList<RNASequence> inSequences = null;
	private String outFile = "";
	private String inFile = "";
//	private LinkedList<RNASequence> ampliconSequences = null;
//	private double redundancy = 0;
	
	public BuildPhyloXMLTree(LinkedList<RNASequence> inSequences, String outFile) {
		this.inSequences = inSequences;
		this.outFile = outFile;
	}
	
	public BuildPhyloXMLTree(String inFile, String outFile) {
		this.inFile = inFile;
		this.outFile = outFile;
	}
/*	
	public BuildPhyloXMLTree(LinkedList<RNASequence> ampliconSequences, double redundancy) {
		this.ampliconSequences = ampliconSequences;
		this.redundancy = redundancy;
	}
*/	
	public void buildTreeFromTaxonomy() {
		try {
			DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
			Document doc = docBuilder.newDocument();
			
			Element root = doc.createElement("phyloxml");
			root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
			root.setAttribute("xsi:schemaLocation", "http://www.phyloxml.org http://www.phyloxml.org/1.10/phyloxml.xsd");
			root.setAttribute("xmlns", "http://www.phyloxml.org");
			doc.appendChild(root);
			Element phyloroot = doc.createElement("phylogeny");
			phyloroot.setAttribute("rooted", "true");
			root.appendChild(phyloroot);
			
			Scanner scanInFile = new Scanner(new FileInputStream(inFile));
			while (scanInFile.hasNextLine()) {
				String currentLine = scanInFile.nextLine();
				String[] currentTaxonomy = currentLine.split(";");
				
				Element currentElement = phyloroot;
				int taxCounter = 0;
				String currentTax = currentTaxonomy[taxCounter];
				NodeList currentChildren = phyloroot.getChildNodes();
				boolean found = true;
				while (found) {
					Element foundElement = findGroup(currentTax, currentChildren);
					if (foundElement != null) {
						currentElement = foundElement;
						currentChildren = currentElement.getChildNodes();
						if (currentTaxonomy.length > (taxCounter+1))
							taxCounter++;
						currentTax = currentTaxonomy[taxCounter];
					}
					else {
						found = false;
					}
				}
				int findString = findStringTax(currentTax, currentTaxonomy);
				
				for (int j = findString; j < currentTaxonomy.length; j++) {
					Element cladeElement = doc.createElement("clade");
					currentElement.appendChild(cladeElement);
					Element taxonomyElement = doc.createElement("taxonomy");
					cladeElement.appendChild(taxonomyElement);
					Element scientNameElement = doc.createElement("scientific_name");
					scientNameElement.setTextContent(currentTaxonomy[j]);
					taxonomyElement.appendChild(scientNameElement);
					currentElement = cladeElement;
				}
				//add tax_embl_name
				Element cladeElement = doc.createElement("clade");
				currentElement.appendChild(cladeElement);
				Element taxonomyElement = doc.createElement("taxonomy");
				cladeElement.appendChild(taxonomyElement);
				Element scientNameElement = doc.createElement("scientific_name");
				scientNameElement.setTextContent("no name");
				taxonomyElement.appendChild(scientNameElement);
				
				Element sequenceElement = doc.createElement("sequence");
				cladeElement.appendChild(sequenceElement);
				Element accessionElement = doc.createElement("accession");
				Attr newAttribute = doc.createAttribute("source");
				newAttribute.setValue("embl");
				accessionElement.setTextContent("no name");
				accessionElement.setAttributeNode(newAttribute);
				sequenceElement.appendChild(accessionElement);
			}
			scanInFile.close();
			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();
			trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
			trans.setOutputProperty(OutputKeys.INDENT, "yes");
			
			FileWriter fw = new FileWriter(outFile);
			StreamResult result = new StreamResult(fw);
			DOMSource source = new DOMSource(doc);
			trans.transform(source, result);
			fw.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void buildTree() {
		try {
			DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
			Document doc = docBuilder.newDocument();
			
			Element root = doc.createElement("phyloxml");
			root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
			root.setAttribute("xsi:schemaLocation", "http://www.phyloxml.org http://www.phyloxml.org/1.10/phyloxml.xsd");
			root.setAttribute("xmlns", "http://www.phyloxml.org");
			doc.appendChild(root);
			Element phyloroot = doc.createElement("phylogeny");
			phyloroot.setAttribute("rooted", "true");
			root.appendChild(phyloroot);
			
			Iterator<RNASequence> seqIter = inSequences.listIterator();
			while (seqIter.hasNext()) {
				RNASequence currentSequence = seqIter.next();
				String[] currentTaxonomy = currentSequence.getTaxonomy().getEMBLTaxonomyPath().split(";");
				Element currentElement = phyloroot;
				int taxCounter = 0;
				String currentTax = currentTaxonomy[taxCounter];
				NodeList currentChildren = phyloroot.getChildNodes();
				boolean found = true;
				while (found) {
					Element foundElement = findGroup(currentTax, currentChildren);
					if (foundElement != null) {
						currentElement = foundElement;
						currentChildren = currentElement.getChildNodes();
						if (currentTaxonomy.length > (taxCounter+1))
							taxCounter++;
						currentTax = currentTaxonomy[taxCounter];
					}
					else {
						found = false;
					}
				}
				int findString = findStringTax(currentTax, currentTaxonomy);
				
				for (int j = findString; j < currentTaxonomy.length; j++) {
					Element cladeElement = doc.createElement("clade");
					currentElement.appendChild(cladeElement);
					Element taxonomyElement = doc.createElement("taxonomy");
					cladeElement.appendChild(taxonomyElement);
					Element scientNameElement = doc.createElement("scientific_name");
					scientNameElement.setTextContent(currentTaxonomy[j]);
					taxonomyElement.appendChild(scientNameElement);
					currentElement = cladeElement;
				}
				//add tax_embl_name
				Element cladeElement = doc.createElement("clade");
				currentElement.appendChild(cladeElement);
				Element taxonomyElement = doc.createElement("taxonomy");
				cladeElement.appendChild(taxonomyElement);
				Element scientNameElement = doc.createElement("scientific_name");
				scientNameElement.setTextContent(currentSequence.getTaxonomy().getID());
				taxonomyElement.appendChild(scientNameElement);
				
				Element sequenceElement = doc.createElement("sequence");
				cladeElement.appendChild(sequenceElement);
				Element accessionElement = doc.createElement("accession");
				Attr newAttribute = doc.createAttribute("source");
				newAttribute.setValue("embl");
				accessionElement.setTextContent(currentSequence.getAccession().getID());
				accessionElement.setAttributeNode(newAttribute);
				sequenceElement.appendChild(accessionElement);
			}
			
			TransformerFactory transfac = TransformerFactory.newInstance();
			Transformer trans = transfac.newTransformer();
			trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
			trans.setOutputProperty(OutputKeys.INDENT, "yes");
			
			FileWriter fw = new FileWriter(outFile);
			StreamResult result = new StreamResult(fw);
			DOMSource source = new DOMSource(doc);
			trans.transform(source, result);
			fw.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static int findStringTax(String currentTax, String[] currentTaxonomy) {
		for (int i = currentTaxonomy.length-1; i >= 0; i--) {
			if (currentTaxonomy[i].equalsIgnoreCase(currentTax))
				return i;
		}
		return 0;
	}
	
	public static Element findGroup(String currentTax, NodeList currentChildren) {
		int numberChildren = currentChildren.getLength();
		for (int i = 0; i < numberChildren; i++) {
			if(currentChildren.item(i).getNodeName().equalsIgnoreCase("clade")) {
				Node clade = currentChildren.item(i);
				Node taxonomy = clade.getFirstChild();
				Node scientName = taxonomy.getFirstChild();
				if (scientName instanceof Element) {
					Element scientNameElement = (Element)scientName;
					if (scientNameElement.getTextContent().equalsIgnoreCase(currentTax)) {
						Element cladeElement = (Element) clade;
						return cladeElement;
					}
				}
			}
		}
		return null;
	}
}
